/**
 * MotionTrajectory 类用于在 Cesium 查看器中创建和管理运动轨迹。
 * 该类封装了轨迹数据处理、实体添加和动画控制等功能。
 */
class MotionTrajectory {
    /**
     * 构造函数，初始化 MotionTrajectory 实例。
     *
     * @param {Cesium.Viewer} viewer - Cesium 查看器实例，用于显示地理数据。
     * @param {Array} data - 轨迹数据数组，每个元素是一个包含经度、纬度和高度的数组。
     * @param {string} modelUri - 模型的 URI，用于在轨迹上显示的模型。
     */
    constructor(viewer, data, modelUri) {
        // 存储传入的 Cesium 查看器实例
        this.viewer = viewer;
        // 存储轨迹数据
        this.data = data;
        // 存储模型的 URI
        this.modelUri = modelUri;
        // 创建一个自定义数据源，用于存储轨迹相关的实体
        this.datasoure = new Cesium.CustomDataSource('entiestestdata');
        // 将自定义数据源添加到查看器中
        this.viewer.dataSources.add(this.datasoure);
        // 初始化轨迹
        // this.initLocus();
    }

    /**
     * 初始化轨迹，包括生成路径数据、添加路径到数据源、创建位置属性和添加带有模型和动画的实体。
     */
    initLocus() {
        // 生成路径数据
        const pathData = this.generatePathData(this.data);
        // 将路径添加到数据源中
        this.addPathToDatasource(this.datasoure, pathData);
        // 创建位置属性，用于控制模型的位置
        const property = this.createPositionProperty(this.data);
        // 获取停止时间，用于动画的结束时间
        const dates = this.getStopTime(this.data);

        // 添加带有模型和动画的实体到数据源
        this.addEntityWithModelAndAnimation(property, dates, this.datasoure);
    }

    /**
     * 从轨迹数据中生成路径数据。
     *
     * @param {Array} data - 轨迹数据数组，每个元素是一个包含经度、纬度和高度的数组。
     * @returns {Array} - 路径数据数组，包含所有经度、纬度和高度值。
     */
    generatePathData(data) {
        let pathData = [];
        // 遍历轨迹数据，将每个元素的经度、纬度和高度添加到路径数据数组中
        data.forEach((item) => {
            pathData.push(item[0], item[1], item[2]);
        });
        return pathData;
    }

    /**
     * 将路径添加到指定的数据源中。
     *
     * @param {Cesium.CustomDataSource} datasoure - 要添加路径的数据源。
     * @param {Array} pathData - 路径数据数组，包含所有经度、纬度和高度值。
     */
    addPathToDatasource(datasoure, pathData) {
        // 在数据源中添加一个实体，用于显示路径
        datasoure.entities.add({
            name: "line",
            polyline: {
                // 将路径数据转换为笛卡尔坐标数组
                positions: Cesium.Cartesian3.fromDegreesArrayHeights(pathData),
                // 路径的颜色
                material: Cesium.Color.YELLOW,
                // 路径的宽度
                width: 1,
                // 高度参考类型
                heightReference: Cesium.HeightReference.NONE,
                // 禁用深度测试的距离
                disableDepthTestDistance: Number.POSITIVE_INFINITY,
            },
        });
    }

    /**
     * 创建一个采样位置属性，用于控制模型的位置，并在每个采样点添加一个点实体。
     *
     * @param {Array} data - 轨迹数据数组，每个元素是一个包含经度、纬度和高度的数组。
     * @returns {Cesium.SampledPositionProperty} - 采样位置属性实例。
     */
    createPositionProperty(data) {
        // 创建一个采样位置属性实例
        let property = new Cesium.SampledPositionProperty();
        // 遍历轨迹数据，为每个点添加采样信息
        data.forEach((item, index) => {
            // 计算当前点的时间
            let time = new Date(Date.now() + index * 10000);
            // 将当前点的经纬度和高度转换为笛卡尔坐标
            let position = Cesium.Cartesian3.fromDegrees(item[0], item[1], item[2]);
            // 为采样位置属性添加一个采样点
            property.addSample(Cesium.JulianDate.fromDate(time), position);
            // 为点实体添加一个特殊标识，方便后续清除
            this.viewer.entities.add({
                position: position,
                point: {
                    // 点的像素大小
                    pixelSize: 10,
                    // 点的颜色
                    color: Cesium.Color.RED,
                    // 点的轮廓宽度
                    outlineWidth: 2,
                    // 点的轮廓颜色
                    outlineColor: Cesium.Color.DARKRED,
                },
                isMotionTrajectoryPoint: true // 添加标识
            });
        });
        // 设置采样位置属性的插值选项
        property.setInterpolationOptions({
            interpolationDegree: 0.01,
            interpolationAlgorithm: Cesium.LagrangePolynomialApproximation,
        });
        return property;
    }

    /**
     * 根据轨迹数据计算停止时间。
     *
     * @param {Array} data - 轨迹数据数组，每个元素是一个包含经度、纬度和高度的数组。
     * @returns {Date} - 停止时间的日期对象。
     */
    getStopTime(data) {
        // 计算停止时间，为当前时间加上轨迹数据长度乘以 10000 毫秒
        const stoptime = new Date(Date.now() + (data.length - 1) * 10000);
        return stoptime;
    }

    /**
     * 添加一个带有模型和动画的实体到指定的数据源中，并设置时钟和动画选项。
     *
     * @param {Cesium.SampledPositionProperty} property - 采样位置属性，用于控制模型的位置。
     * @param {Date} dates - 停止时间的日期对象，用于动画的结束时间。
     * @param {Cesium.CustomDataSource} datasource - 要添加实体的数据源。
     */
    addEntityWithModelAndAnimation(property, dates, datasource) {
        // 在查看器中添加一个带有模型和动画的实体
        this.viewer.entities.add({
            // 设置实体的可用时间区间
            availability: new Cesium.TimeIntervalCollection([
                new Cesium.TimeInterval({
                    start: Cesium.JulianDate.fromDate(new Date()),
                    stop: Cesium.JulianDate.fromDate(dates),
                }),
            ]),
            // 设置实体的 z 索引
            zIndex: 1,
            // 设置实体的位置属性
            position: property,
            // 设置实体的方向属性，根据速度计算方向
            orientation: new Cesium.VelocityOrientationProperty(property),
            model: {
                // 模型的 URI
                uri: this.modelUri,
                // 模型的缩放比例
                scale: 10.0,
            },
            path: {
                // 路径的分辨率
                resolution: 1,
                // 路径的材质
                material: new Cesium.PolylineGlowMaterialProperty({
                    glowPower: 0.1,
                    color: Cesium.Color.YELLOW
                }),
                // 路径的宽度
                width: 10
            }
        });
        // 设置查看器时钟的当前时间
        this.viewer.clock.currentTime = Cesium.JulianDate.fromDate(new Date());
        // 设置查看器时钟的停止时间
        this.viewer.clock.stopTime = Cesium.JulianDate.fromDate(dates);
        // 设置查看器时钟的范围为固定
        this.viewer.clock.clockRange = Cesium.ClockRange.CLAMPED;
        // 让查看器飞到指定的数据源
        this.viewer.flyTo(datasource);
        // 启用查看器时钟的动画功能
        this.viewer.clock.shouldAnimate = true;
    }

    /**
     * 清除轨迹相关的数据源和实体。
     */
    clear() {
        // 从查看器中移除自定义数据源
        this.viewer.dataSources.remove(this.datasoure, true);
        // 存储需要移除的实体
        const entitiesToRemove = [];
        // 遍历查看器中的所有实体，找出需要移除的实体
        this.viewer.entities.values.forEach((entity) => {
            if (entity.name === "line" || entity.model || entity.isMotionTrajectoryPoint) {
                entitiesToRemove.push(entity);
            }
        });
        // 移除所有需要移除的实体
        entitiesToRemove.forEach((entity) => {
            this.viewer.entities.remove(entity);
        });
    }
}

export default MotionTrajectory;